#include <opencv2/opencv.hpp>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <iostream>

struct buffer_object
{
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint32_t handle;
    uint32_t size;
    uint32_t *vaddr;
    uint32_t fb_id;
};

struct buffer_object buf;

static int modeset_create_fb(int fd, struct buffer_object *bo)
{
    struct drm_mode_create_dumb create = {};
    struct drm_mode_map_dumb map = {};

    create.width = bo->width;
    create.height = bo->height;
    create.bpp = 32;
    drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

    bo->pitch = create.pitch;
    bo->size = create.size;
    bo->handle = create.handle;
    drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
                 bo->handle, &bo->fb_id);
    printf("bo->pitch = %d, bo->size = %d, bo->handle=%d\r\n", bo->pitch, bo->size, bo->handle);
    map.handle = create.handle;
    drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

    bo->vaddr = (uint32_t *)mmap(0, create.size, PROT_READ | PROT_WRITE,
                                 MAP_SHARED, fd, map.offset);

    memset(bo->vaddr, 0x0, bo->size);

    return 0;
}

// 旋转和缩放图片的函数
int process_image(int fd, const char *image_path, struct buffer_object *bo, int target_width, int target_height)
{
    // 1. 加载图片
    cv::Mat img = cv::imread(image_path, 1); // 读取图片
    if (img.empty())
    {
        std::cerr << "Failed to load image" << std::endl;
        return -1;
    }

    // 2. 旋转图片
    cv::Point2f center(img.cols / 2.0, img.rows / 2.0); // 旋转中心点 (图片的中心)
    double angle = 90.0;                                // 旋转角度 (顺时针 90 度)
    double scale = 1.0;                                 // 缩放因子，设置为1表示保持原始大小

    // 获取旋转矩阵
    cv::Mat rotation_matrix = cv::getRotationMatrix2D(center, angle, scale);

    // 计算旋转后图像的边界框大小
    cv::Rect bbox = cv::RotatedRect(center, img.size(), angle).boundingRect();
    rotation_matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;
    rotation_matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;

    // 进行旋转
    cv::Mat rotated_img;
    cv::warpAffine(img, rotated_img, rotation_matrix, bbox.size());

    // 3. 缩放图片到指定尺寸
    cv::Mat scaled_img;
    cv::resize(rotated_img, scaled_img, cv::Size(target_width, target_height)); // 缩放到目标尺寸

    // 4. 将 OpenCV 图像数据传给 DRM framebuffer
    std::cout << "scaled_img.channels: " << scaled_img.channels() << std::endl;
    if (scaled_img.channels() != 3)
    {
        std::cerr << "Image must have 3 channels (RGB)" << std::endl;
        return -1;
    }
    else if (scaled_img.channels() == 3)
    {
        cv::cvtColor(scaled_img, scaled_img, cv::COLOR_RGB2BGRA); // 转换为 4 通道，并设置默认透明度
    }
    std::cout << "scaled_img.channels: " << scaled_img.channels() << std::endl;
    printf("scaled_img.cols = %d, scaled_img.rows = %d\r\n", scaled_img.cols, scaled_img.rows);

    modeset_create_fb(fd, bo);

    // OpenCV 图像数据与 framebuffer 数据的对应
    uint32_t *fb_data = bo->vaddr; // framebuffer 显存的起始地址

    int fb_pitch = bo->pitch; // framebuffer 的每行字节数，通常是 width * 4（32bpp）

    // 将 OpenCV 图像数据拷贝到 framebuffer
    // 将 OpenCV 图像数据拷贝到 framebuffer
    for (int y = 0; y < target_height; ++y)
    {
        for (int x = 0; x < target_width; ++x)
        {
            // 获取 OpenCV 图像中每个像素的 RGBA 值
            cv::Vec4b color = scaled_img.at<cv::Vec4b>(y, x); // 获取 RGBA 格式的像素
            uint8_t r = color[2];  // Red 通道
            uint8_t g = color[1];  // Green 通道
            uint8_t b = color[0];  // Blue 通道
            uint8_t a = color[3];  // Alpha 通道（透明度）

            // 计算 framebuffer 中的偏移量
            int fb_offset = y * fb_pitch + x * 4;      // 每行偏移量 + 每个像素的字节偏移
            uint32_t pixel = (a << 24) | (b << 16) | (g << 8) | r; // BGRA 格式

            // 将像素数据写入 framebuffer
            fb_data[fb_offset / 4] = pixel;
        }
    }
    return 0;
}

static uint32_t get_property_id(int fd, drmModeObjectProperties *props,
                                const char *name)
{
    drmModePropertyPtr property;
    uint32_t i, id = 0;

    for (i = 0; i < props->count_props; i++)
    {
        property = drmModeGetProperty(fd, props->props[i]);
        if (!strcmp(property->name, name))
            id = property->prop_id;
        drmModeFreeProperty(property);

        if (id)
            break;
    }

    return id;
}

// 释放 framebuffer
static void modeset_destroy_fb(int fd, struct buffer_object *bo)
{
    struct drm_mode_destroy_dumb destroy = {};
    drmModeRmFB(fd, bo->fb_id);
    munmap(bo->vaddr, bo->size);
    destroy.handle = bo->handle;
    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}

// 初始化与显示图像的主函数
int main(int argc, char **argv)
{
    if (argc < 2)
    {
        std::cerr << "Usage: ./drm_display <image_path>" << std::endl;
        return -1;
    }

    int fd;
    drmModeConnector *conn;
    drmModeRes *res;
    drmModePlaneRes *plane_res;
    drmModeObjectProperties *props;
    drmModeAtomicReq *req;
    uint32_t conn_id;
    uint32_t crtc_id;
    uint32_t plane_id;
    uint32_t blob_id;
    uint32_t property_crtc_id;
    uint32_t property_active;
    uint32_t property_mode_id;
    uint32_t property_fb_id;
    uint32_t property_crtc_x;
    uint32_t property_crtc_y;
    uint32_t property_crtc_w;
    uint32_t property_crtc_h;
    uint32_t property_src_x;
    uint32_t property_src_y;
    uint32_t property_src_w;
    uint32_t property_src_h;

    fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
    if (fd < 0)
    {
        std::cerr << "Failed to open DRM device" << std::endl;
        return -1;
    }

    res = drmModeGetResources(fd);
    crtc_id = res->crtcs[3];
    conn_id = res->connectors[2];

    drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
    plane_res = drmModeGetPlaneResources(fd);
    if (!plane_res)
    {
        fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
                strerror(errno));
        return -ENOENT;
    }
    plane_id = plane_res->planes[0];

    conn = drmModeGetConnector(fd, conn_id);
    buf.width = conn->modes[0].hdisplay;
    buf.height = conn->modes[0].vdisplay;
    printf("buf.width: %d, buf.height: %d\r\n", buf.width, buf.height);

    // 处理图片，旋转并缩放
    if (process_image(fd, argv[1], &buf, 1080, 1920) < 0)
    {
        close(fd);
        return -1;
    }

    // modeset_create_fb(fd, &buf);

    drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);

    props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
    property_crtc_id = get_property_id(fd, props, "CRTC_ID");
    drmModeFreeObjectProperties(props);

    props = drmModeObjectGetProperties(fd, crtc_id, DRM_MODE_OBJECT_CRTC);
    property_active = get_property_id(fd, props, "ACTIVE");
    property_mode_id = get_property_id(fd, props, "MODE_ID");
    drmModeFreeObjectProperties(props);

    drmModeCreatePropertyBlob(fd, &conn->modes[0], sizeof(conn->modes[0]), &blob_id);

    req = drmModeAtomicAlloc();
    drmModeAtomicAddProperty(req, crtc_id, property_active, 1);
    drmModeAtomicAddProperty(req, crtc_id, property_mode_id, blob_id);
    drmModeAtomicAddProperty(req, conn_id, property_crtc_id, crtc_id);
    drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
    drmModeAtomicFree(req);

    printf("drmModeAtomicCommit SetCrtc\n");
    getchar();

    /* get plane properties */
    props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
    property_crtc_id = get_property_id(fd, props, "CRTC_ID");
    property_fb_id = get_property_id(fd, props, "FB_ID");
    property_crtc_x = get_property_id(fd, props, "CRTC_X");
    property_crtc_y = get_property_id(fd, props, "CRTC_Y");
    property_crtc_w = get_property_id(fd, props, "CRTC_W");
    property_crtc_h = get_property_id(fd, props, "CRTC_H");
    property_src_x = get_property_id(fd, props, "SRC_X");
    property_src_y = get_property_id(fd, props, "SRC_Y");
    property_src_w = get_property_id(fd, props, "SRC_W");
    property_src_h = get_property_id(fd, props, "SRC_H");
    drmModeFreeObjectProperties(props);

    /* atomic plane update */
    req = drmModeAtomicAlloc();
    drmModeAtomicAddProperty(req, plane_id, property_crtc_id, crtc_id);
    drmModeAtomicAddProperty(req, plane_id, property_fb_id, buf.fb_id);
    // 显示屏幕的切割
    drmModeAtomicAddProperty(req, plane_id, property_crtc_x, 0);
    drmModeAtomicAddProperty(req, plane_id, property_crtc_y, 0);
    drmModeAtomicAddProperty(req, plane_id, property_crtc_w, 1080);
    drmModeAtomicAddProperty(req, plane_id, property_crtc_h, 1920);
    // framebuff的切割
    drmModeAtomicAddProperty(req, plane_id, property_src_x, 0);
    drmModeAtomicAddProperty(req, plane_id, property_src_y, 0);
    drmModeAtomicAddProperty(req, plane_id, property_src_w, 1080 << 16);
    drmModeAtomicAddProperty(req, plane_id, property_src_h, 1920 << 16);
    drmModeAtomicCommit(fd, req, 0, NULL);
    drmModeAtomicFree(req);

    printf("drmModeAtomicCommit SetPlane\n");
    getchar();

    modeset_destroy_fb(fd, &buf);

    drmModeFreeConnector(conn);
    drmModeFreePlaneResources(plane_res);
    drmModeFreeResources(res);

    close(fd);

    return 0;
}
